﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Optimization;
using XWeb.Web.Sass;

namespace XWeb.Web.Optimization.Sass
{
	public class SassBundleTransform : IBundleTransform
	{
		public void Process(BundleContext context, BundleResponse response)
		{
			response.Content = SassCompiler.CompileString(response.Content);
			response.ContentType = "text/css";
		}
	}

	public class SassBundle : StyleBundle
	{
		public SassBundle(string virtualPath)
			: base(virtualPath)
		{
			Builder = SassBundleBuilder.Instance;
		}
	}

	public class SassBundleBuilder : IBundleBuilder
	{
		internal static IBundleBuilder Instance = new SassBundleBuilder();

		public OutputStyle OutputStyle { get; set; }

		public SassBundleBuilder()
		{
			OutputStyle = OutputStyle.Nested;
		}

		private static Dictionary<string, string> GetInstrumentedBundlePreamble(string boundaryValue)
		{
			var dictionary = new Dictionary<string, string>();
			dictionary["Bundle"] = "System.Web.Optimization.Bundle";
			dictionary["Boundary"] = boundaryValue;
			return dictionary;
		}

		private static string GetBoundaryIdentifier(Bundle bundle)
		{
			return Convert.ToBase64String(Encoding.Unicode.GetBytes((bundle.Transforms == null || bundle.Transforms.Count > 0 ? bundle.Transforms[0].GetType().FullName : "System.Web.Optimization.NoTransform").GetHashCode().ToString(CultureInfo.InvariantCulture)));
		}

		private static string GetInstrumentedFileHeaderFormat(string boundaryValue)
		{
			return "/* " + boundaryValue + " \"{0}\" */";
		}

		private static string GetFileHeader(string appPath, FileInfo file, string fileHeaderFormat)
		{
			if (string.IsNullOrEmpty(fileHeaderFormat))
				return string.Empty;
			var str = (!file.FullName.StartsWith(appPath, StringComparison.OrdinalIgnoreCase) ? file.FullName : file.FullName.Replace(appPath, "~/")).Replace('\\', '/');
			return string.Format(CultureInfo.InvariantCulture, fileHeaderFormat, new object[] {str}) + "\r\n";
		}

		private static string GetFileSafeVirtualPath(string appPath, FileInfo file)
		{
			if (file.FullName.StartsWith(appPath, StringComparison.OrdinalIgnoreCase))
				return file.FullName.Replace(appPath, "~/").Replace('\\', '/');
			return "~/" + file.Name;
		}

		private static string GetAppPath(BundleContext context)
		{
			if (context.HttpContext == null || context.HttpContext.Request == null)
				return string.Empty;
			return context.HttpContext.Request.PhysicalApplicationPath;
		}

		private static string GenerateBundlePreamble(string bundleHash)
		{
			var instrumentedBundlePreamble = GetInstrumentedBundlePreamble(bundleHash);
			var stringBuilder = new StringBuilder();
			stringBuilder.Append("/* ");
			foreach (string index in instrumentedBundlePreamble.Keys)
				stringBuilder.Append(index + "=" + instrumentedBundlePreamble[index] + ";");
			stringBuilder.Append(" */");
			return stringBuilder.ToString();
		}

		public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<FileInfo> files)
		{
			if (files == null)
				return string.Empty;
			if (context == null)
				throw new ArgumentNullException("context");
			string appPath = GetAppPath(context);
			var stringBuilder = new StringBuilder();
			string str = "";
			if (context.EnableInstrumentation)
			{
				str = GetBoundaryIdentifier(bundle);
				stringBuilder.AppendLine(GenerateBundlePreamble(str));
			}
			foreach (var file in files)
			{
				if (context.EnableInstrumentation)
					stringBuilder.Append(GetFileHeader(appPath, file, GetInstrumentedFileHeaderFormat(str)));
				stringBuilder.Append(CompileFile(context, file, GetFileSafeVirtualPath(appPath, file)));
				if (!string.IsNullOrEmpty(bundle.ConcatenationToken))
					stringBuilder.Append(bundle.ConcatenationToken);
				else
					stringBuilder.AppendLine();
			}
			return stringBuilder.ToString();
		}

		internal class VirtualFileResolver: ISourceProvider
		{
			public string Path { get; private set; }
			private readonly FileInfo _fileInfo;
			private string _content;
			private readonly BundleContext _context;

			public VirtualFileResolver(BundleContext context, string path, FileInfo fileInfo)
			{
				this._context = context;
				Path = path;
				_fileInfo = fileInfo;
			}

			public string Content
			{
				get { return _content ?? (_content = File.ReadAllText(_fileInfo.FullName)); }
			}

			public ISourceProvider Resolve(string path)
			{
				if (_context == null || _context.HttpContext == null || _context.HttpContext.Server == null)
					return null;

				var dir = VirtualPathUtility.GetDirectory(Path);
				var newPath = VirtualPathUtility.Combine(dir, path);
				var fullPath = _context.HttpContext.Server.MapPath(newPath);

				if (!File.Exists(fullPath))
					return null;

				return new VirtualFileResolver(_context, newPath, new FileInfo(fullPath));
			}
		}

		protected virtual string CompileFile(BundleContext context, FileInfo fileInfo, string virtualPath)
		{
			return SassCompiler.CompileSource(new VirtualFileResolver(context, virtualPath, fileInfo), OutputStyle);
		}
	}
}
